package jobase

type LinkList[T any] struct {
	root ListNode[T]
	len  int
}

func NewLinkList[T any]() *LinkList[T] {
	return new(LinkList[T]).Init()
}

func (l *LinkList[T]) Init() *LinkList[T] {
	l.root.next = &l.root
	l.root.prev = &l.root
	l.len = 0
	return l
}

func (l *LinkList[T]) Len() int {
	return l.len
}

func (l *LinkList[T]) Front() *ListNode[T] {
	if l.len == 0 {
		return nil
	}
	return l.root.next
}

func (l *LinkList[T]) Back() *ListNode[T] {
	if l.len == 0 {
		return nil
	}
	return l.root.prev
}

func (l *LinkList[T]) Remove(n *ListNode[T]) T {
	if n.list == l {
		l.remove(n)
	}

	return n.data
}

func (l *LinkList[T]) PushFront(v T) *ListNode[T] {
	l.lazyInit()
	return l.insertValue(v, &l.root)
}

func (l *LinkList[T]) PushBack(v T) *ListNode[T] {
	l.lazyInit()
	return l.insertValue(v, l.root.prev)
}

func (l *LinkList[T]) InsertBefore(v T, mark *ListNode[T]) *ListNode[T] {
	l.lazyInit()
	return l.insertValue(v, mark.prev)
}

func (l *LinkList[T]) InsertAfter(v T, mark *ListNode[T]) *ListNode[T] {
	l.lazyInit()
	return l.insertValue(v, mark)
}

func (l *LinkList[T]) MoveToFront(n *ListNode[T]) {
	if n.list != l || l.root.next == n {
		return
	}

	l.move(n, &l.root)
}

func (l *LinkList[T]) MoveToBack(n *ListNode[T]) {
	if n.list != l || l.root.prev == n {
		return
	}

	l.move(n, l.root.prev)
}

func (l *LinkList[T]) MoveBefore(n, mark *ListNode[T]) {
	if n.list != l || n == mark || mark.list != l {
		return
	}

	l.move(n, mark.prev)
}

func (l *LinkList[T]) MoveAfter(n, mark *ListNode[T]) {
	if n.list != l || n == mark || mark.list != l {
		return
	}

	l.move(n, mark)
}

func (l *LinkList[T]) PushBackList(lst *LinkList[T]) {
	l.lazyInit()
	for i, n := lst.Len(), lst.Front(); i > 0; i, n = i-1, n.Next() {
		l.insertValue(n.data, l.root.prev)
	}
}

func (l *LinkList[T]) PushFrontList(lst *LinkList[T]) {
	l.lazyInit()
	for i, n := lst.Len(), lst.Back(); i > 0; i, n = i-1, n.Prev() {
		l.insertValue(n.data, &l.root)
	}
}

func (l *LinkList[T]) PushBackSlice(s []T) {
	l.lazyInit()
	for _, v := range s {
		l.PushBack(v)
	}
}

func (l *LinkList[T]) ToSlice() []T {
	var result []T

	for n := l.Front(); n != nil; n = n.Next() {
		result = append(result, n.data)
	}

	return result
}

func (l *LinkList[T]) lazyInit() {
	if l.root.next == nil {
		l.Init()
	}
}

func (l *LinkList[T]) insert(n, at *ListNode[T]) *ListNode[T] {
	n.prev = at
	n.next = at.next
	n.prev.next = n
	n.next.prev = n
	n.list = l
	l.len++
	return n
}

func (l *LinkList[T]) insertValue(v T, at *ListNode[T]) *ListNode[T] {
	return l.insert(&ListNode[T]{data: v}, at)
}

func (l *LinkList[T]) remove(n *ListNode[T]) {
	n.prev.next = n.next
	n.next.prev = n.prev
	n.next = nil
	n.prev = nil
	n.list = nil
	l.len--
}

func (l *LinkList[T]) move(n, at *ListNode[T]) {
	if n == at {
		return
	}

	n.prev.next = n.next
	n.next.prev = n.prev

	n.prev = at
	n.next = at.next
	n.prev.next = n
	n.next.prev = n
}
